﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Mimoza.PluginLayer;

namespace Mimoza.AgentLayer
{
    public class PolicyManager : BaseObject
    {
        public PolicyManager(Configuration config)
            : base(config)
        {
            m_pluginManager = new Mimoza.PluginLayer.ExecutePluginManager();
            m_pluginManager.Init();

            LastSyncId = Configuration.AgentDatabase.GetHostEffectivePolicyId();
        }

        override public void Init()
        {
            Configuration.ObjectLayer.PolicyRepository.OnObjectChanged += OnPolicyChanged;
        }

        override public void Shutdown()
        {
            Configuration.ObjectLayer.PolicyRepository.OnObjectChanged -= OnPolicyChanged;

            m_pluginManager.Shutdown();
        }

        public Guid LastSyncId
        {
            get { return m_lastSyncId; }
            set { m_lastSyncId = value; }
        }

        public void ApplySyncItem(PolicySyncItem syncItem)
        {
            List<ObjectLayer.Policy> policies = Configuration.AgentDatabase.GetRootPolicies();
            
            LastSyncId = syncItem.SyncId;

            Configuration.AgentDatabase.ReplaceServerPolicies(syncItem.Policies, LastSyncId);

            PluginResult pluginResult = new PluginResult();
            
            ApplyEffectivePoliciesToPlugins(syncItem, pluginResult);

            Configuration.AgentDatabase.SaveLocalHostModificationId(syncItem.HostModificationId);

            ProcessPluginResult.Process(Configuration, pluginResult);
        }

        void ApplyEffectivePoliciesToPlugins(PolicySyncItem syncItem, PluginResult pluginResult)
        {
            Common.Logger.Log.Info("PolicyManager.ApplyPoliciesToPlugins...");

            if (!IsNeedToApplyPolicies(syncItem.SyncId, syncItem.HostModificationId))
            {
                Common.Logger.Log.Info("PolicyManager.ApplyPoliciesToPlugins. syncId is equal sync is not need.");
                return;
            }

            List<ObjectLayer.Policy> effectivePolicies = 
                new List<Mimoza.ObjectLayer.Policy>(Configuration.AgentDatabase.GetLocalHostContainer().GetEffectivePolicy());
            List<ObjectLayer.Policy> syncedPolicies = Configuration.AgentDatabase.GetSyncPolicies();

            Common.Logger.Log.Info("PolicyManager.ApplyPoliciesToPlugins effectivePolicies.size - " +
                effectivePolicies.Count + " syncedPolicies.sizze - " + syncedPolicies.Count + ".");

            foreach (ObjectLayer.Policy policy in effectivePolicies)
            {
                ObjectLayer.ObjectLayerEventType operation;
                if (IsNeedToApplyToPlugin(policy, syncedPolicies, out operation))
                {
                    ApplyToPlugin(policy, operation, pluginResult);
                }
            }

            foreach (ObjectLayer.Policy existPolicy in syncedPolicies)
            {
                bool bExistInNew = (effectivePolicies.FindIndex(p => p.ID == existPolicy.ID) >= 0);

                if (!bExistInNew)
                {
                    ApplyToPlugin(existPolicy, Mimoza.ObjectLayer.ObjectLayerEventType.ObjectDeleted, pluginResult);
                }
            }

            Configuration.AgentDatabase.ReplaceSyncPolicies(effectivePolicies, syncItem.SyncId);

            Common.Logger.Log.Info("PolicyManager.ApplyPoliciesToPlugins...OK");
        }

        bool IsNeedToApplyPolicies(Guid syncId, Guid hostModificationId)
        {
            if (Configuration.AgentDatabase.GetSyncEffectivePolicyId() != syncId ||
                Configuration.AgentDatabase.GetLocalHostModificationId() != hostModificationId) return true;
            else return false;
        }

        void ApplyToPlugin(ObjectLayer.Policy policy, ObjectLayer.ObjectLayerEventType operationType,
            PluginResult pluginResult)
        {
            Common.Logger.Log.Info("PolicyManager.ApplyToPlugin. Id - '" +
                policy.PluginID + "' operationType - " + operationType.ToString() + " ...");

            Mimoza.PluginLayer.IExecutePlugin pPlugin = m_pluginManager.Get(policy.PluginID);
            if (pPlugin == null)
            {
                Common.Logger.Log.Warn("PolicyManager.ApplyToPlugin. Error plugin wasn't found. Id - '" +
                    policy.PluginID + "'");
                return;
            }

            try
            {
                switch (operationType)
                {
                    case Mimoza.ObjectLayer.ObjectLayerEventType.ObjectInserted:
                        pPlugin.Add(policy);
                        break;
                    case Mimoza.ObjectLayer.ObjectLayerEventType.ObjectUpdated:
                        pPlugin.Update(policy);
                        break;
                    case Mimoza.ObjectLayer.ObjectLayerEventType.ObjectDeleted:
                        pPlugin.Remove(policy);
                        break;
                }

                Mimoza.PluginLayer.PluginResult res = Mimoza.PluginLayer.PluginResultStore.GetResult();
                if (res != null)
                {                    
                    if (res.NeedToReboot) pluginResult.NeedToReboot = true;
                    PluginResultStore.ResetResult();
                }
            }
            catch (System.Exception e)
            {
                Common.Logger.Log.Warn("PolicyManager.ApplyToPlugin. Error was occured - '" +
                    e.ToString() + "'.");
                return;
            }

            Common.Logger.Log.Info("PolicyManager.ApplyToPlugin. Id - '" +
                policy.PluginID + "'...OK");
        }

        void OnPolicyChanged(object sender, Mimoza.ObjectLayer.ObjectLayer_ObjectChangedEventArgs eventArg)
        {
            if (!(eventArg.Object is Mimoza.ObjectLayer.Policy))
            {
                Common.Logger.Log.Error("PolicyManager.OnPolicyChanged ERROR Object is not policy.");
                return;
            }

            Mimoza.ObjectLayer.Policy modifiedPolicy = eventArg.Object as Mimoza.ObjectLayer.Policy;

            if (modifiedPolicy.Record == null)
            {
                Common.Logger.Log.Error("PolicyManager.OnPolicyChanged ERROR Policy is empty.");
                return;
            }

            if (modifiedPolicy.Parent!=null && modifiedPolicy.Parent.IsSystem)
            {
                Common.Logger.Log.Debug("PolicyManager.OnPolicyChanged policy ('{0}') from system container modified.",
                    modifiedPolicy.Name);
                return;
            }

            if (modifiedPolicy.Parent != Configuration.AgentDatabase.GetLocalHostContainer())
            {
                Common.Logger.Log.Debug("PolicyManager.OnPolicyChanged policy ('{0}') is not from local host container.",
                    modifiedPolicy.ID.ToString() );
                return;
            }

            Common.Logger.Log.Info("PolicyManager.OnPolicyChanged opertion - '" + eventArg.EventType.ToString() +
                "' id - '" + modifiedPolicy.ID.ToString() + "' Name - '" + modifiedPolicy.Name + "' Operation - " +
                eventArg.EventType.ToString() + ".");

            Configuration.AgentDatabase.SaveSyncEffectivePolicyId(Guid.NewGuid());

            PluginLayer.PluginResult result = new Mimoza.PluginLayer.PluginResult();
            ApplyEffectivePoliciesToPlugins(new PolicySyncItem(LastSyncId), result);

            ProcessPluginResult.Process(Configuration, result);
        }

        static bool IsNeedToApplyToPlugin(ObjectLayer.Policy policy, List<ObjectLayer.Policy> syncedPolicies,
            out ObjectLayer.ObjectLayerEventType operation)
        {
            int index = (-1);
            bool bExist = ((index = syncedPolicies.FindIndex(p => p.ID == policy.ID)) >= 0);
            bool bNeedToApply = true;
            if (bExist && syncedPolicies[index].DataModificationID == policy.DataModificationID) bNeedToApply = false;

            operation = bExist ? Mimoza.ObjectLayer.ObjectLayerEventType.ObjectUpdated :
                        Mimoza.ObjectLayer.ObjectLayerEventType.ObjectInserted;

            return bNeedToApply;
        }

        Guid m_lastSyncId = new Guid();

        Mimoza.PluginLayer.ExecutePluginManager m_pluginManager;
    }

    public class PolicySyncItem
    {
        public PolicySyncItem()
        {
        }

        public PolicySyncItem(Guid lastSyncId)
        {
            SyncId = lastSyncId;
        }

        public Guid SyncId
        {
            get { return m_syncId; }
            set { m_syncId = value; }
        }

        public Guid HostModificationId
        {
            get { return m_hostModificationId; }
            set { m_hostModificationId = value; }
        }


        public void AddPolicy(ObjectLayer.Policy policy)
        {
            m_policyList.Add(policy);
        }

        public List<ObjectLayer.Policy> Policies
        {
            get { return m_policyList; }
        }

        Guid m_syncId;
        Guid m_hostModificationId = Guid.Empty;
        List<ObjectLayer.Policy> m_policyList = new List<Mimoza.ObjectLayer.Policy>();
    }
}
